home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 8: LINUX Games
/
Linux Cubed Series 8 - LINUX Games.iso
/
games
/
video
/
pictetri.src
/
pictetri
/
pictetris-src
/
pictetris.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-12-22
|
19KB
|
557 lines
/***************************************************************************\
|* *|
|* pict.c: A version of Tetris to run on Linux SVGAlib console. *|
|* Main module *|
|* *|
|* Authors: Mike Taylor (mirk@uk.ac.warwick.cs) & *|
|* Arturo Espinosa (arturo@nuclecu.unam.mx) *|
|* Started: Fri May 26 12:26:05 BST 1989 (tetris for terminals) *|
|* Dic 1, 1995 (pictetris) *|
|* *|
\***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <jlib.h>
#include <signal.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <string.h>
/* Here goes what's needed to make this puppy work under Linux. */
#define F_TLOCK 2
#define BADSIG2 -1 /* appended 2 just to avoid the BADSIG redef. warning */
void l_bcopy (const void* s, void* d, int n)
{if (n > 0) memmove (d, s, (size_t) n);}
#include "pictetris.h"
#include "utils.h"
#include "screen.h"
#include "game.h"
/*-------------------------------------------------------------------------*/
extern time_t time ();
extern char *ctime ();
extern char *malloc ();
extern char *getenv ();
extern char *getlogin ();
extern int rand_lines; /* Garbage lines at the start of the game */
extern int next_box; /* Flag for the next-piece box */
extern int black_bg; /* Flag for the black background */
extern int sound; /* Flag for sound */
/*-------------------------------------------------------------------------*/
int in_curses = 0; /* Set to 1 after initialisation */
int rotate_backwards = 0; /* If set non-zero, rotate clockwise */
int no_hiscores = 0; /* Number of hi-scores in the table */
int no_shown = NO_SHOWN; /* Number of hi-scores to list */
int game_level = 0; /* Number of free pieces */
int rand_lines = 0; /* Number of random lines (new option) */
int score; /* Accumulated game score */
int no_pieces; /* Number of pieces dropped so far */
int no_levels; /* Number of levels filled & deleted */
int total_time = 50000; /* Allow 1/4 second before falling: used to be long & 200000*/
int total_time2=0; /* This one stores the speed level */
int black_bg = 0; /* You want no picture on the playing area? */
int next_box =1; /* Couldn't forget a next-piece box */
int sound=1; /* Sound flag */
char prog_name[LINELEN]; /* Will be the basename of argv[0] */
char user_name[NAMELEN]; /* From environment: TTNAME or NAME */
char user_code[CODELEN]; /* From getpwuid(getuid())->pw_name */
int board[GAME_DEPTH+4][GAME_WIDTH];
struct score_ent hi_scores[NO_HISCORES];
char tc_string[LINELEN]; /* Needed as static storage for the awful */
char *so_str; /* ... tgetstr() function. so_str and ... */
char *se_str; /* ... se_str point into it. */
/***************************************************************************\
|* *|
|* This function is called if a SIGHUP, SIGINT or SIGTERM is caught, *|
|* and merely returns the user to standard terminal modes, (ie. exits *|
|* from curses(3X) before die()ing. *|
|* *|
\***************************************************************************/
void signal_end ()
{
if (in_curses)
print_msg ("Aborted!");
die (LE_OK, "");
}
/***************************************************************************\
|* *|
|* The function get_scores() reads the contents of the global array *|
|* hi_scores from the file named in the #definition of SCORE_FILE. *|
|* It also sets no_hiscores to the number of scores in the table. *|
|* *|
\***************************************************************************/
void get_scores ()
{
int fd;
struct stat stat_buf;
if ((fd = open (SCORE_FILE, O_RDONLY)) == -1) {
if (errno != ENOENT)
die (LE_OPEN, "couldn't open(2) high-score file for reading");
else {
no_hiscores = 0;
return;
}
}
if (fstat (fd, &stat_buf) == -1)
die (LE_STAT, "couldn't stat(2) high-score file");
no_hiscores = stat_buf.st_size/sizeof (struct score_ent);
if (read (fd, (char*) hi_scores, (int) stat_buf.st_size) == -1) {
(void) perror ("read()");
die (LE_READ, "couldn't read(2) high-score file");
}
(void) close (fd);
}
/***************************************************************************\
|* *|
|* The function print_scores() gets the table in from the disk_file, *|
|* and prints it in a nice, human-readable format. *|
|* *|
\***************************************************************************/
void print_scores ()
{
int i;
get_scores ();
if (no_hiscores == 0)
print_msg ("There are no high-scores (yet!)");
else {
draw_border(0,0,37,20);
draw_border(38,0,150,20);
draw_border(151,0,191,20);
draw_border(192,0,236,20);
draw_border(237,0,281,20);
draw_border(282,0,319,20);
draw_border(0,21,37,199);
draw_border(38,21,150,199);
draw_border(151,21,191,199);
draw_border(192,21,236,199);
draw_border(237,21,281,199);
draw_border(282,21,319,199);
/* buff_draw_string is a Jlib function */
buff_draw_string(offscreen,"Rank Name Score Pieces Levels Type"
,8,8,1);
for (i = 0; i < ((no_hiscores < no_shown) ? no_hiscores : no_shown); i++)
buff_draw_string(offscreen,form("%5d %-*.*s %6d %7d %7d %5d",
i+1, NAMELEN, NAMELEN, hi_scores[i].name,hi_scores[i].score,
hi_scores[i].no_pieces, hi_scores[i].no_levels,hi_scores[i].game_level),
8,29+LINE(i*2),1);
}
}
/***************************************************************************\
|* *|
|* The function update_file puts a lock on the high-score-file, and *|
|* then (if successful), reads the high-scores, inserts the current *|
|* score in the table if it's good enough, and writes it back if it *|
|* has changed, finally removing the lock. It returns the player's *|
|* position in the table, or 0 if he is unplaced. If we were unable *|
|* to get the lock-file, it returns -1. *|
|* *|
|* I'm about to do some mods allowing compilation to be done with *|
|* the flag -DLOCKF to do the mutual exclusion using lockf(3) instead *|
|* a lock-file. This will be unneccesary on most systems, but here on *|
|* the Warwick systems, some users have titchy quotas that cause them *|
|* to be unable to create a lock-file, and thus to use the high-score *|
|* table. I'm using lockf(3) instead of flock(2) since it works across *|
|* machines. *|
|* *|
|* Apologies for the cruddy way I've written this function. It has had *|
|* bits added onto it in a very ad-hoc way, including the #ifdef'd bits *|
|* that determine what locking mechanism is used, and the result is, *|
|* shall we say, sub-optimal elegance. Particularly nasty is that way *|
|* that when the lockf(3)ing locking mechanism is used, we maintain *|
|* two open file-descriptors at once on the same file, but it's the *|
|* quickest and easiest way to use the existing get_scores() function, *|
|* and it does at least work. Since I don't anticipate anything else *|
|* significant being added to the function, I'm going to leave it as it *|
|* is, and not tidy it up unless I feel *really* guilty in the morning. *|
|* *|
\***************************************************************************/
int update_file ()
{
int i = 0, j = 0, k = 0; /* Sundry re-usable loop-indices */
int score_fd; /* The fd through which we write new file */
int lock_fd; /* If LOCKF is defined, we use this as */
/* an auxiliary fd on the SCORE_FILE, one */
/* that stays open all the time, so we can */
/* use lockf(). Otherwise, it is the fd */
/* that we open to the LOCK_FILE */
(void) umask (0000); /* 000 octal, just to make the point */
#ifdef LOCKF
if ((lock_fd = open (SCORE_FILE, O_RDWR | O_CREAT, 0666)) == -1)
die (LE_OPEN, "Couldn't open(2) score-file for lockf()");
while (i++ < 5) { /* Make up to five attempts */
if (lockf (lock_fd, F_TLOCK, 0) != -1)
break; /* If we succeed, then carry on */
/* Otherwise, if not due to exclusivity */
if (errno != EAGAIN) { /* then die with a system error */
print_msg ("lockf(3) error!");
(void) get_key ();
return (-1);
}
print_msg ("Hi-score access:");
sleep (1); /* Back off and wait ... */
print_msg (""); /* Then try again */
}
#else /* LOCKF */
while (i++ < 5) { /* Make up to five attempts */
if ((lock_fd = open (LOCK_FILE, O_CREAT | O_EXCL, 0666)) != -1)
break; /* If we succeed, then carry on */
/* Otherwise, if not due to exclusivity */
if (errno != EEXIST) { /* then die with a system error */
print_msg ("open(2) error!");
(void) get_key ();
return (-1);
}
print_msg ("Hi-score access:");
sleep (1); /* Back off and wait ... */
print_msg (""); /* Then try again */
}
#endif /* LOCKF */
if (i > 5) /* If we tried 5 times unsuccessfully, */
return (-1); /* Then give up and return -1 instead */
get_scores ();
for (i = 0; i < no_hiscores; i++) {
if (((!strcmp (user_code, hi_scores[i].code)) &&
(game_level == hi_scores[i].game_level)) &&
((score < hi_scores[i].score) ||
((score == hi_scores[i].score) &&
((no_pieces < hi_scores[i].no_pieces) ||
((no_pieces == hi_scores[i].no_pieces) &&
(no_levels < hi_scores[i].no_levels)))))) {
i = NO_HISCORES; /* If the same user has a better score */
break; /* on the same level, then drop this one */
}
if ((score > hi_scores[i].score) ||
((score == hi_scores[i].score) &&
((no_pieces > hi_scores[i].no_pieces) ||
((no_pieces == hi_scores[i].no_pieces) &&
((no_levels > hi_scores[i].no_levels) ||
((no_levels == hi_scores[i].no_levels) &&
((game_level >= hi_scores[i].game_level)))))))) /* Lisp :-) */
break; /* i is the new position of the player */
}
if (i == NO_HISCORES) { /* If we looped off the end of the array */
(void) close (lock_fd); /* then the score isn't good enough: */
#ifndef LOCKF /* Automagically removes advisory lockf() */
(void) unlink (LOCK_FILE);
#endif /* LOCKF */
return (0);
}
/* If there is a matching score lower down */
/* the file, set j to it, (otherwise to i) */
for (j = NO_HISCORES-1; j >= i; j--)
if ((!strcmp (user_code, hi_scores[j].code)) &&
(game_level == hi_scores[j].game_level))
break;
/* No duplicate score found, so just */
if (j < i) { /* shunt up all other scores. */
for (j = NO_HISCORES-1; j > i; j--)
l_bcopy ((char*) &hi_scores[j-1], (char*) &hi_scores[j],
sizeof (struct score_ent));
if (no_hiscores < NO_HISCORES)
no_hiscores++;
}
else { /* j points at a duplicate score of the */
for (k = j; k > i; k--) { /* new one, so shift bits between them */
l_bcopy ((char*) &hi_scores[k-1], (char*) &hi_scores[k],
sizeof (struct score_ent));
}
}
(void) strcpy (hi_scores[i].name, user_name);
(void) strcpy (hi_scores[i].code, user_code);
hi_scores[i].score = score;
hi_scores[i].no_pieces = no_pieces;
hi_scores[i].no_levels = no_levels;
hi_scores[i].game_level = game_level+rand_lines+(50000-total_time)/2500;
if ((score_fd = open (SCORE_FILE, O_WRONLY | O_CREAT, 0666)) == -1) {
perror ("open");
die (LE_OPEN, "couldn't open(2) score-file for writing");
}
if (write (score_fd, (char*) hi_scores, no_hiscores*sizeof
(struct score_ent)) == -1) {
perror ("write");
die (LE_WRITE, "couldn't write(2) to score-file");
}
(void) close (score_fd);
(void) close (lock_fd);
#ifndef LOCKF
(void) unlink (LOCK_FILE);
#endif /* LOCKF */
return (i+1);
}
/***************************************************************************\
|* *|
|* save_options() creates a little script file named pict which sets *|
|* the current options so that when the user executes it, these will *|
|* remain. *|
|* *|
\***************************************************************************/
void save_options() {
FILE *optfile;
optfile=fopen("pict","w");
fwrite("#! /bin/sh\n",11,1,optfile);
fprintf(optfile,"pictetris ");
if (black_bg) fprintf (optfile, "-B ");
if (!next_box) fprintf (optfile, "-n ");
if (!sound) fprintf (optfile, "-S ");
if (rotate_backwards) fprintf (optfile, "-b ");
if (game_level!=0) fprintf (optfile,"-f%d ",game_level);
if (rand_lines!=0) fprintf (optfile,"-r%d ",rand_lines);
if (total_time2!=0) fprintf (optfile,"-l%d ",total_time2);
fclose(optfile);
chmod("pict",00751);
print_msg("Done... press any key.");
myrefresh();
get_keyboard_key();
}
/***************************************************************************\
|* *|
|* The function get_key() reads a character from the keyboard, and *|
|* performs some simple processing on it. If it's an 's', it lists *|
|* the high-score table. Otherwise, it returns 1 for a 'q' or 'n', *|
|* and 0 for anything else. *|
|* *|
\***************************************************************************/
int get_key ()
{
char ch;
ch=toupper(get_keyboard_key());
if ((ch == 's') || (ch == 'S')) {
clear_area();
print_scores ();
myrefresh ();
get_keyboard_key();
}
draw_area ();
flush_keyboard();
return ((ch == 'N') || (ch == 'S') || (ch == 'Q'));
}
/***************************************************************************\
|* *|
|* The main() function handles initialisation, gets keys and names from *|
|* the environent, parses command-line arguments and so on. It then *|
|* goes into the main loop of calling play_game(), and asking if the *|
|* player wants another game, and so on. *|
|* *|
\***************************************************************************/
main (argc, argv)
int argc;
char **argv;
{
int i;
char *cp; /* Temporary pointer for getenv() */
time_t ignore_me; /* Storage for time for random seed. */
struct passwd *pw_ptr; /* Used with getuid() to find usercode */
char optkey; /* Stores the key pressed during option menu */
(void) srandom ((int) time (&ignore_me));
(void) strcpy (prog_name, basename (argv[0]));
if ((i = getuid ()) == -1)
die (LE_GETUID, "couldn't geteuid(2)");
if ((pw_ptr = getpwuid (i)) == NULL)
die (LE_GETPW, "couldn't get password entry");
(void) strncpy (user_code, pw_ptr->pw_gecos, CODELEN-1);
user_code[CODELEN-1] = '\0';
if ((cp = getenv ("TTNAME")) == NULL)
if ((cp = getenv ("NAME")) == NULL)
cp = user_code;
(void) strncpy (user_name, cp, NAMELEN-1);
user_name[NAMELEN-1] = '\0';
for (i = 1; i < argc; i++)
switch (argv[i][0]) {
case '-':
switch (argv[i][1]) {
case 's':
if (argv[i][2] != '\0')
if (((no_shown = atoi (argv[i]+2)) < 1) ||
(no_shown > NO_HISCORES)) {
static char tmp[LINELEN]; /* To stop recursive form() */
(void) sprintf (tmp, "Number of scores must be between 1 and %d",
NO_HISCORES);
die (LE_LEVEL, tmp);
}
setup_curses();
setup_screen();
clear_area();
print_scores ();
myrefresh();
get_key(); /* Flag '-s': print scores, then exit. */
die (LE_OK,"");
case 'b':
rotate_backwards = 1;
break;
case 'f':
if (argv[i][2] == '\0')
goto USAGE_ERROR;
else
if (((game_level = atoi (argv[i]+2)) < -10) || (game_level > 20))
die (LE_LEVEL, "Free-falling pieces must be between -10 and 20");
break;
case 'r':
if (argv[i][2] == '\0')
goto USAGE_ERROR;
else
if (((rand_lines=atoi (argv[i]+2)) < 0) || (rand_lines > 16))
die (LE_LEVEL, "Random lines must be between 0 and 16");
break;
case 'B':
black_bg=1;
break;
case 'n':
next_box=0;
break;
case 'S':
sound=0;
break;
case 'l':
if (argv[i][2] == '\0')
goto USAGE_ERROR;
else
if (((total_time2 = atoi (argv[i]+2)) < 0) || (total_time2 > 20))
die (LE_LEVEL, "Starting speed must be between 0 and 20");
break;
default:
goto USAGE_ERROR;
}
break;
default:
USAGE_ERROR:
die (LE_USAGE, form ("Usage: %s [ -s ] [ -S ] [ -n ] [ -b ] [ -B ] [ -f# ] [ -r# ] [ -l# ]\n\n -s: show scores\n -S: No sound (sometimes annoying)\n -n: No next-piece box :(\n -b: rotate clockwise\n -B: black background\n -f: free falling pieces\n -r: random lines\n -l: starting speed\n", prog_name));
}
if (((int) signal (SIGHUP, signal_end) == BADSIG2) ||
((int) signal (SIGINT, signal_end) == BADSIG2) ||
((int) signal (SIGTERM, signal_end) == BADSIG2))
die (LE_SIGNAL, "couldn't set up signal-handling");
setup_curses();
draw_title();
myrefresh();
flush_keyboard();
get_keyboard_key();
while (1) {
draw_title();
do {
flush_keyboard();
draw_options(black_bg,next_box,sound,
rand_lines,total_time2,game_level);
optkey=toupper(get_keyboard_key());
switch ((int) optkey) {
case 'B': black_bg=(black_bg==1) ? 0 : 1;
break;
case 'S': sound=(sound==1) ? 0 : 1;
break;
case 'N': next_box=(next_box==1) ? 0 : 1;
break;
case 'L': total_time2=(total_time2==20) ? 0 : total_time2+1;
break;
case 'F': game_level=(game_level==20) ? -10 : game_level+1;
break;
case 'R': rand_lines=(rand_lines==16) ? 0 : rand_lines+1;
break;
case 'V':
setup_screen();
clear_area();
print_scores();
myrefresh();
get_key();
draw_title();
break;
case 'K':
save_options();
break;
case 'Q': die (LE_OK, "");
break;
}
} while (optkey!='C');
setup_screen ();
clear_board ();
draw_area();
flush_keyboard();
while (1) {
total_time=50000-total_time2*2500;
play_game ();
if ((i = update_file ()) > 0) {
static char tmp[LINELEN]; /* To stop recursive form() phelgming */
(void) sprintf (tmp, form("Score ranks #%d", i));
print_msg (tmp);
}
if (i < 0)
print_msg ("Save-score failed!");
if (i != 0) {
flush_keyboard();
if (get_key ())
break;
}
print_msg (" Again? (Y/N/Scores) ");
flush_keyboard();
if (get_key ())
break;
}
}
}
/*-------------------------------------------------------------------------*/